package service;

import javafx.util.Pair;
import util.FileUtil;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Path;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

public class ClassUtils {

    /**
     * 扫描包路径下的所有class文件
     *
     * @param pkgName 包名
     * @param pkgPath 包对应的绝对地址
     * @param classes 保存包路径下class的集合
     */
    public static void findClassesByFile(String pkgName, String pkgPath, Set<Class<?>> classes) throws ClassNotFoundException {

        File dir = new File(pkgPath);
        if (!dir.exists() || !dir.isDirectory()) {
            return;
        }

        // 过滤获取目录，or class文件
        File[] dirFiles = dir.listFiles(pathname -> pathname.isDirectory() || pathname.getName().endsWith("class"));
        if (dirFiles == null || dirFiles.length == 0) {
            return;
        }

        String className;
        for (File f : dirFiles) {
            if (f.isDirectory()) {
                findClassesByFile(pkgName + "." + f.getName(),
                        pkgPath + "/" + f.getName(),
                        classes);
                continue;
            }

            // 获取类名，干掉 ".class" 后缀
            className = f.getName();
            className = className.substring(0, className.length() - 6);

            // 加载类
            Class<?> clz = extLoadClass(pkgPath, pkgName + "." + className);
            if (clz != null) {
                classes.add(clz);
            }
        }
    }

    /**
     * 扫描包路径下的所有class文件
     *
     * @param pkgName 包名
     * @param jarFilePath     jar文件
     * @param classes 保存包路径下class的集合
     */
    public static void findClassesByJar(String pkgName, String jarFilePath, Set<Class<?>> classes) throws IOException, ClassNotFoundException {
        String pkgDir = pkgName.replace('.', '/');

        JarFile jar=new JarFile(jarFilePath);

        Enumeration<JarEntry> entry = jar.entries();

        JarEntry jarEntry;
        String name, className;
        while (entry.hasMoreElements()) {
            jarEntry = entry.nextElement();

            name = jarEntry.getName();
            if (name.charAt(0) == '/') {
                name = name.substring(1);
            }

            if (jarEntry.isDirectory() || !name.startsWith(pkgDir) || !name.endsWith(".class")) {
                // 非指定包路径， 非class文件
                continue;
            }
            System.out.println(name);
            // 去掉后面的".class", 将路径转为package格式
            className = name.substring(0, name.length() - 6);
            //Class<?> clz = loadClass(className.replace("/", "."), new String[]{jarFilePath.substring(0,jarFilePath.lastIndexOf("/"))});
            Class<?> clz = loadClass(className.replace("/", "."),jarFilePath);
            if (clz != null) {
                classes.add(clz);
            }
        }
    }

    public static Class<?> extLoadClass(String extClassPath,String fullClzName) {
        Path externalClassPath = Path.of(extClassPath);
        ExternalClassloader classloader = new ExternalClassloader(externalClassPath);

        // 使用classloader加载外部类
        try {
            return classloader.loadClass(fullClzName);
        }catch (ClassNotFoundException e) {
            System.out.printf("load class error! path: %s, clz: %s, e:%s\n", extClassPath, fullClzName, e.getMessage());
            //return extClassLoader.loadClass(fullClzName);
        }
        return null;
    }

    public static Class<?> loadClass(String fullClzName,String... classPaths) {
        //类加载器
        ExternalJarLoader jarLoader =new ExternalJarLoader(classPaths);
        try {
            return jarLoader.loadClass(fullClzName);
        } catch (ClassNotFoundException e) {
            System.out.printf("load class error! clz: %s, path:%s, e:%s\n", fullClzName,classPaths[0], e.getMessage());
            //return appClassLoader.loadClass(fullClzName);
        }
        return null;
    }

    public static Class<?> loadClass(String fullClzName, String classPath) {
        URLClassLoader classLoader=null;
        try {
            if (classPath.endsWith(".jar")) {
                //使用jar包方式加载
                classLoader = new URLClassLoader(new URL[]{new File(classPath).toURI().toURL()});
            } else {
                // 创建URLClassLoader实例
                classLoader = new URLClassLoader(new URL[]{new URL("file:" + classPath)});
            }
        }catch (MalformedURLException e){
            System.out.printf("path: %s,e:%s\n",classPath,e.getMessage());
        }

        // 使用URLClassLoader加载指定类
        try {
            if (classLoader!=null) return classLoader.loadClass(fullClzName);
        }catch (ClassNotFoundException e) {
            System.out.printf("load class error! path: %s, clz: %s, e:%s\n", classPath, fullClzName, e.getMessage());
            //return extClassLoader.loadClass(fullClzName);
        }
        return null;
    }

    public static Set scanClass(Pair<String,String> pagPair) {

        String pkgName=pagPair.getKey();
        String pkgPath=pagPair.getValue();

        if (pkgPath.startsWith(".")) {
            pkgPath= FileUtil.GetCurPath()+pkgPath.substring(1);
        }

        Set<Class<?>> allClass = new HashSet<>();
        if (pkgPath.endsWith(".jar")) {
            try {
                System.out.printf("class path: %s\n",pkgPath);
                findClassesByJar(pkgName, pkgPath, allClass);
            }catch (IOException e) {
                System.out.printf("find classes by Jar, e:%s",e.getMessage());
            } catch (ClassNotFoundException e) {
                throw new RuntimeException(e);
            }
        }
        else {
            try {
                System.out.printf("class path: %s\n",pkgPath);
                findClassesByFile(pkgName, pkgPath, allClass);
            }catch (ClassNotFoundException e) {
                throw new RuntimeException(e);
            }
        }

        return allClass;
    }


}
